package it.canraid.control;

import java.util.ArrayList;

import android.os.Handler;
import android.os.Message;

public abstract class CAgent extends Thread implements IAgent, IHandlerCommunication {
    private boolean run = false;
    private boolean fired = false;
    private int queueLength = 0;
    private byte[] bufferOutput = new byte[queueLength];
    private ArrayList<Handler> handlersToNotifies = new ArrayList<Handler>();
    private long timeWaitPolling = 0;

    public CAgent(long timeWaitPolling) {
	setDaemon(false);
	this.timeWaitPolling = timeWaitPolling;
    }

    //
    // ABSTRACT
    //

    public abstract int getAgentName();

    public abstract void close();

    protected abstract boolean __loop();

    protected abstract boolean __setup();

    protected abstract boolean __implementMyHandler(Message msg);

    //
    // IMPLEMENTED
    //

    protected Handler[] __getHandlersToNotifies() {
	Handler[] aux = null;
	synchronized (handlersToNotifies) {
	    aux = new Handler[handlersToNotifies.size()];
	    for (int i = 0; i < aux.length; i++)
		aux[i] = handlersToNotifies.get(i);
	}
	return aux;
    }

    public Handler getMyHandler() {

	return new Handler() {
	    public void handleMessage(Message msg) {
		if (msg.what == getAgentName() && msg.arg1 == IHandlerCommunication.REQUEST_AGENT_START && msg.arg2 == IHandlerCommunication.TYPE_STANDARD) {
		    if (!isRunning() && !isFired()) {
			startPolling();
		    }
		}
		if (msg.what == getAgentName() && msg.arg1 == IHandlerCommunication.REQUEST_AGENT_STOP && msg.arg2 == IHandlerCommunication.TYPE_STANDARD) {
		    stopPolling();
		}

		if (msg.what == getAgentName() && msg.arg1 == IHandlerCommunication.ACTION_TO_AGENT && msg.arg2 == IHandlerCommunication.TYPE_STANDARD) {
		    byte[] data = (byte[]) msg.obj;
		    pushInQueue(data);
		}

		__implementMyHandler(msg);
	    }
	};
    }

    public void addExternalHandler(Handler handler) {
	synchronized (handlersToNotifies) {
	    if (!this.handlersToNotifies.contains(handler)) {
		this.handlersToNotifies.add(handler);
	    }
	}
    }

    public void pushInQueue(byte[] data) {

	if (data == null)
	    return;

	if (data.length < 1)
	    return;

	synchronized (bufferOutput) {
	    // Impilatura
	    byte[] newBuffer = new byte[bufferOutput.length + data.length];
	    System.arraycopy(bufferOutput, 0, newBuffer, 0, bufferOutput.length);
	    System.arraycopy(data, 0, newBuffer, bufferOutput.length, data.length);
	    bufferOutput = newBuffer;
	    queueLength = bufferOutput.length;
	}
    }

    public void stopPolling() {
	run = false;
    }

    public void startPolling() {
	this.start();
    }

    public void run() {
	run = true;
	fired = true;
	Handler[] handlers = __getHandlersToNotifies();
	try {
	    boolean internalRun = __setup();

	    for (int i = 0; i < handlers.length && internalRun; i++)
		handlers[i].obtainMessage(getAgentName(), IHandlerCommunication.ACTION_AGENT_STARTED, IHandlerCommunication.TYPE_STANDARD, 1).sendToTarget();

	    while (run && internalRun) {
		Thread.sleep(timeWaitPolling);
		internalRun = __loop();
	    }
	} catch (Exception e) {
	} finally {
	    run = false;
	    handlers = __getHandlersToNotifies();
	    for (int i = 0; i < handlers.length; i++)
		handlers[i].obtainMessage(getAgentName(), IHandlerCommunication.ACTION_AGENT_STOPPED, IHandlerCommunication.TYPE_STANDARD, 1).sendToTarget();

	    close();
	}
    }

    public int getQueueLength() {
	return queueLength;
    }

    protected byte[] __getCopyBufferOutput(boolean commit) {

	byte[] newBuffer = null;
	synchronized (bufferOutput) {
	    newBuffer = new byte[bufferOutput.length];
	    System.arraycopy(bufferOutput, 0, newBuffer, 0, bufferOutput.length);
	    if (commit) {
		bufferOutput = new byte[0];
		queueLength = 0;
	    }
	}
	return newBuffer;
    }

    protected boolean __commitCopyBufferOutput(int len) {

	if ((bufferOutput.length - len) < 0)
	    return false;

	byte[] newBuffer = null;
	synchronized (bufferOutput) {
	    newBuffer = new byte[bufferOutput.length - len];
	    System.arraycopy(bufferOutput, len, newBuffer, 0, (bufferOutput.length - len));
	    bufferOutput = newBuffer;
	    queueLength = bufferOutput.length;
	}
	return true;
    }

    public boolean isFired() {
	return fired;
    }

    public boolean isRunning() {
	return run;
    }

    public boolean isBurned() {
	if (fired && !run)
	    return true;

	return false;
    }
}
